#pragma once
#include <queue>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <chrono>
/*
* This head file declares some thread safe container types.
*/

namespace TS
{
	using namespace std;
	using namespace std::chrono;
	/*Thread Safe Queue*/
	template<typename T>
	class ThreadSafeQueue
	{
		mutable mutex m_mut;
		queue<shared_ptr<T>> m_qData;
		condition_variable m_cond;
	private:
		void CopyImp(const ThreadSafeQueue& other)
		{
			queue<shared_ptr<T>> tq = other.m_qData;
			while (!tq.empty()) {
				auto tmp = tq.front();
				tq.pop();
				m_qData.push(shared_ptr<T>(new T(*tmp)));
			}
		}
	public:
		/*
		* ctor
		*/
		ThreadSafeQueue() {}
		ThreadSafeQueue(const ThreadSafeQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			CopyImp(other);
		}
		ThreadSafeQueue& operator=(const ThreadSafeQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			lock_guard<mutex> mlk(m_mut);
			if (this == &other) return *this;
			CopyImp(other);
			return *this;
		}
		/*
		* push data into the queue.
		*/
		void Push(IN T newvalue)
		{
			shared_ptr<T> data(
				make_shared<T>(move(newvalue))
			);
			lock_guard<mutex> lk(m_mut);
			m_qData.push(data);
			m_cond.notify_one();
		}
		/*
		* wait until a data to pop out.
		*/
		void WaitAndPop(OUT T& value)
		{
			unique_lock<mutex> ulk(m_mut);
			m_cond.wait(ulk, [this] {return !m_qData.empty(); });
			value = move(*m_qData.front());
			m_qData.pop();
		}
		/*
		* another version of wait and pop
		*/
		shared_ptr<T> WaitAndPop()
		{
			unique_lock<mutex> ulk(m_mut);
			m_cond.wait(ulk, [this] {return !m_qData.empty(); });
			shared_ptr<T> res = m_qData.front();
			m_qData.pop();
			return res;
		}
		/*
		* try to pop value.
		* return true if it is successfully popped.
		*/
		bool TryPop(OUT T& value)
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return false;
			value = move(*m_qData.front());
			m_qData.pop();
			return true;
		}
		/*
		* another version of try pop
		*/
		shared_ptr<T> TryPop()
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return shared_ptr<T>();
			shared_ptr<T> res = m_qData.front();
			m_qData.pop();
			return res;
		}
		/*
		* return true if the queue is empty.
		*/
		bool Empty()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.empty();
		}
		/*
		* return the size of queue
		*/
		size_t Size()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.size();
		}
		/*
		* return the max size of queue
		*/
		size_t Limit()const
		{
			return size_t(-1);
		}
	};

	/*Thread Safe Priority Queue*/
	template<class T>
	class ThreadSafePriorityQueue
	{
		template<class U = void>
		struct LessForSharedPtr
		{
			constexpr bool operator()(const U& _Left, const U& _Right)const {
				return *(_Left.get()) < *(_Right.get());
			}
		};
	private:
		mutable mutex m_mut;
		priority_queue<shared_ptr<T>, vector<shared_ptr<T>>, LessForSharedPtr<shared_ptr<T>>> m_qData;
		condition_variable m_cond;
	private:
		void CopyImp(const ThreadSafePriorityQueue& other)
		{
			auto tq = other.m_qData;
			while (!tq.empty()) {
				auto tmp = tq.top();
				tq.pop();
				m_qData.push(shared_ptr<T>(new T(*tmp)));
			}
		}
	public:
		/*
		* ctor
		*/
		ThreadSafePriorityQueue() {}
		ThreadSafePriorityQueue(const ThreadSafePriorityQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			CopyImp(other);
		}
		ThreadSafePriorityQueue& operator=(const ThreadSafePriorityQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			lock_guard<mutex> mlk(m_mut);
			if (this == &other) return *this;
			CopyImp(other);
			return *this;
		}
		/*
		* push data into the queue.
		*/
		void Push(IN T newvalue)
		{
			shared_ptr<T> data(
				make_shared<T>(move(newvalue))
			);
			lock_guard<mutex> lk(m_mut);
			m_qData.push(move(data));
			m_cond.notify_one();
		}
		/*
		* wait until a data to pop out.
		*/
		void WaitAndPop(OUT T& value)
		{
			unique_lock<mutex> ulk(m_mut);
			m_cond.wait(ulk, [this] {return !m_qData.empty(); });
			value = move(*m_qData.top());
			m_qData.pop();
		}
		shared_ptr<T> WaitAndPop()
		{
			unique_lock<mutex> ulk(m_mut);
			m_cond.wait(ulk, [this] {return !m_qData.empty(); });
			shared_ptr<T> res = m_qData.top();
			m_qData.pop();
			return res;
		}
		/*
		* try to pop value.
		* return true if it is successfully popped.
		*/
		bool TryPop(OUT T& value)
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return false;
			value = move(*m_qData.top());
			m_qData.pop();
			return true;
		}
		/*
		* another version of try pop
		*/
		shared_ptr<T> TryPop()
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return shared_ptr<T>();
			shared_ptr<T> res = m_qData.top();
			m_qData.pop();
			return res;
		}
		/*
		* return true if the queue is empty.
		*/
		bool Empty()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.empty();
		}
		/*
		* return the limit of the queue
		*/
		size_t Limit()const
		{
			return size_t(-1);
		}
	};

	/*Thread Safe Synchronize Queue*/
	template<class T>
	class ThreadSafeSynchronousQueue
	{
		mutable mutex m_mut;
		size_t m_iMaxSize;
		queue<shared_ptr<T>> m_qData;
		condition_variable m_cond_not_full;
		condition_variable m_cond_not_empty;
	private:
		void CopyImp(const ThreadSafeSynchronousQueue& other) {
			queue<shared_ptr<T>> tq = other.m_qData;
			while (!tq.empty()) {
				auto tmp = tq.front();
				tq.pop();
				m_qData.push(shared_ptr<T>(new T(*tmp)));
			}
		}
	public:
		ThreadSafeSynchronousQueue(size_t maxSize = 10) :m_iMaxSize(maxSize) {}
		ThreadSafeSynchronousQueue(const ThreadSafeSynchronousQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			CopyImp(other);
		}
		ThreadSafeSynchronousQueue& operator=(const ThreadSafeSynchronousQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			lock_guard<mutex> mlk(other.m_mut);
			if (this == &other) return *this;
			CopyImp(other);
			return *this;
		}
		void Push(IN T newvalue)
		{
			shared_ptr<T> data(
				make_shared<T>(move(newvalue))
			);
			unique_lock<mutex> lk(m_mut);
			m_cond_not_full.wait(lk, [this] {return this->m_qData.size() < m_iMaxSize; });
			m_qData.push(data);
			m_cond_not_empty.notify_one();
		}
		/*
		* try to pop an element, return false if it's empty.
		*/
		bool TryPop(OUT T& value)
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return false;
			value = move(*m_qData.front());
			m_qData.pop();
			m_cond_not_full.notify_one();
			return true;
		}
		/*
		* wait for an element to pop out, wait until the queue is not empty.
		*/
		void WaitAndPop(OUT T& value)
		{
			unique_lock<mutex> ulk(m_mut);
			m_cond_not_empty.wait(ulk, [this] {return !this->m_qData.empty(); });
			value = move(*m_qData.front());
			m_qData.pop();
			m_cond_not_full.notify_one();
		}
		/*
		* return the size of the queue
		*/
		size_t Size()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.size();
		}
		/*
		* return the max size of the queue
		*/
		size_t Limit()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_iMaxSize;
		}
		/*
		* set the max size of the queue
		*/
		void SetLimit(size_t limit)
		{
			lock_guard<mutex> lk(m_mut);
			m_iMaxSize = limit;
		}
	};

	/*Thread Safe Delayed Queue: queue with delayed time*/
	template<class T>
	class ThreadSafeDelayedQueue
	{
		template<class K = void>
		struct LessForDelayQueue
		{
			constexpr bool operator()(const K& _Left, const K& _Right)const {
				return _Left.first > _Right.first;
			}
		};
		template<class K>
		using _MPair = std::pair<time_point<steady_clock>, shared_ptr<K>>;

		mutable std::mutex m_mut;
		priority_queue<_MPair<T>, vector<_MPair<T>>, LessForDelayQueue<_MPair<T>>> m_qData;
	public:
		ThreadSafeDelayedQueue() {}
		ThreadSafeDelayedQueue(const ThreadSafeDelayedQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			m_qData = other.m_qData;
		}
		ThreadSafeDelayedQueue& operator=(const ThreadSafeDelayedQueue& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			lock_guard<mutex> mlk(m_mut);
			if (this == &other) return *this;
			m_qData = other.m_qData;
			return *this;
		}
		/*
		* push data with delayed time to be ready to pop
		*/
		template<class D, class K>
		void Push(IN T newvalue, chrono::duration<D,K> delay)
		{
			lock_guard<mutex> lk(m_mut);
			m_qData.push(
				make_pair(steady_clock::now() + delay, make_shared<T>(move(newvalue)))
			);
		}
		/*
		* push data with zero delayed time to be ready to pop
		*/
		void Push(IN T newvalue)
		{
			lock_guard<mutex> lk(m_mut);
			m_qData.push(
				make_pair(steady_clock::now(), make_shared<T>(move(newvalue)))
			);
		}
		/*
		* try to pop a timeout value, return ture if success
		*/
		bool TryPop(OUT T& value)
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return false;
			auto tp = m_qData.top();
			auto now = steady_clock::now();
			if (tp.first > now)
				return false;
			value = move(*(tp.second));
			m_qData.pop();
			return true;
		}
		/*
		* return true if top of the queue is timed out
		* if pt is not null and the queue is not empty, record the most recent due time of the top element
		*/
		bool IsTimeOut(steady_clock::time_point* pt = nullptr)const
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty()) return false;
			auto tp = m_qData.top();
			auto now = steady_clock::now();
			if (pt) {
				*pt = tp.first;
			}
			return tp.first < now;
		}
		//return the top element due time, if queue is empty, then simply return now()
		steady_clock::time_point RecentDueTime()const
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty()) return steady_clock::now();
			auto tp = m_qData.top();
			return tp.first;
		}
		bool Empty()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.empty();
		}
		size_t Size()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.size();
		}
		size_t Limit()const
		{
			return size_t(-1);
		}
	};

	/*Thread Safe Vector*/
	template<typename T>
	class ThreadSafeVector
	{
		mutable mutex m_mut;
		vector<shared_ptr<T>> m_qData;
	private:
		void CopyImp(const ThreadSafeVector& other)
		{
			if (!other.m_qData.empty()) {
				size_t len = other.m_qData.size();
				m_qData.resize(len);
				for (size_t i = 0; i < len; ++i) {
					m_qData[i] = shared_ptr<T>(new T(*other.m_qData[i]));
				}
			}
		}
	public:
		/*
		* ctor
		*/
		ThreadSafeVector() {}
		ThreadSafeVector(const ThreadSafeVector& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			CopyImp(other);
		}
		ThreadSafeVector& operator=(const ThreadSafeVector& other)
		{
			lock_guard<mutex> lk(other.m_mut);
			lock_guard<mutex> mlk(m_mut);
			if (this == &other) return *this;
			CopyImp(other);
			return *this;
		}
		/*
		* push data into the vector.
		*/
		void PushBack(IN T newvalue)
		{
			shared_ptr<T> data(
				make_shared<T>(move(newvalue))
			);
			lock_guard<mutex> lk(m_mut);
			m_qData.push_back(data);
		}
		/*
		* try to pop value.
		* return true if it is successfully popped.
		*/
		bool PopBack(OUT T& value)
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return false;
			value = move(*m_qData.back());
			m_qData.pop_back();
			return true;
		}
		shared_ptr<T> PopBack()
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty())
				return shared_ptr<T>();
			shared_ptr<T> res = m_qData.back();
			m_qData.pop_back();
			return res;
		}
		bool Empty()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.empty();
		}
		size_t Size()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.size();
		}
		void ReSize(size_t len)
		{
			lock_guard<mutex> lk(m_mut);
			m_qData.resize(len);
		}
		size_t MaxSize()const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.max_size();
		}
		//note returned shared_ptr may be unsafe because other thread may modify the value at i position.
		shared_ptr<T> At(size_t i)const
		{
			lock_guard<mutex> lk(m_mut);
			return m_qData.at(i);
		}
		/*
		* set value at location i, if vec[i] == oldvalue, then swap and return ture
		*/
		bool CAS(size_t i, const T& oldvalue, T newvalue)
		{
			lock_guard<mutex> lk(m_mut);
			if (i < m_qData.size() && *m_qData[i] == oldvalue)
			{
				m_qData[i] = make_shared<T>(move(newvalue));
				return true;
			}
			else
				return false;
		}
		/*
		* set value at location i, return false if i exceeds the vector length
		*/
		bool SetValueAt(size_t i, T newvalue)
		{
			lock_guard<mutex> lk(m_mut);
			if (m_qData.empty() || i >= m_qData.size()) return false;
			m_qData[i] = make_shared<T>(move(newvalue));
			return true;
		}
		/*
		* find first value equals target, return the pos. -1 indicate not find
		*/
		int Find(const T& target)const
		{
			lock_guard<mutex> lk(m_mut);
			for (size_t i = 0; i < m_qData.size(); ++i) {
				if (*m_qData.at(i).get() == target) return (int)i;
			}
			return -1;
		}
	};

}

template<class T>
using TSQueue = TS::ThreadSafeQueue<T>;

template<class T>
using TSPriorityQueue = TS::ThreadSafePriorityQueue<T>;

template<class T>
using TSSynchronousQueue = TS::ThreadSafeSynchronousQueue<T>;

template<class T>
using TSDelayedQueue = TS::ThreadSafeDelayedQueue<T>;

template<class T>
using TSVector = TS::ThreadSafeVector<T>;